While native code range-checks the requested address, possibly
authorkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Thu, 2 Mar 2006 15:06:51 +0000 (16:06 +0100)
committerkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Thu, 2 Mar 2006 15:06:51 +0000 (16:06 +0100)
truncates the range, and then reads/writes page-wise until possibly
encountering a -EFAULT issue, Xen code accessed the whole range in a
single step, thus not allowing partially succeeding accesses.

From: Jan Beulich

Signed-off-by: Keir Fraser <keir@xensource.com>
linux-2.6-xen-sparse/drivers/xen/char/mem.c

index 36101743580b3f08f5156bce3a206bc2a50344d1..61d79911f0768e8ecb7944784ba08a0fd6c2428b 100644 (file)
@@ -43,49 +43,85 @@ static inline int uncached_access(struct file *file)
 static ssize_t read_mem(struct file * file, char __user * buf,
                        size_t count, loff_t *ppos)
 {
-       unsigned long i, p = *ppos;
-       ssize_t read = -EFAULT;
+       unsigned long p = *ppos, ignored;
+       ssize_t read = 0, sz;
        void __iomem *v;
 
-       if ((v = ioremap(p, count)) == NULL) {
+       while (count > 0) {
                /*
-                * Some programs (e.g., dmidecode) groove off into weird RAM
-                * areas where no table scan possibly exist (because Xen will
-                * have stomped on them!). These programs get rather upset if
-                 * we let them know that Xen failed their access, so we fake
-                 * out a read of all zeroes. :-)
+                * Handle first page in case it's not aligned
                 */
-               for (i = 0; i < count; i++)
-                       if (put_user(0, buf+i))
+               if (-p & (PAGE_SIZE - 1))
+                       sz = -p & (PAGE_SIZE - 1);
+               else
+                       sz = PAGE_SIZE;
+
+               sz = min_t(unsigned long, sz, count);
+
+               if ((v = ioremap(p, sz)) == NULL) {
+                       /*
+                        * Some programs (e.g., dmidecode) groove off into weird RAM
+                        * areas where no tables can possibly exist (because Xen will
+                        * have stomped on them!). These programs get rather upset if
+                        * we let them know that Xen failed their access, so we fake
+                        * out a read of all zeroes. :-)
+                        */
+                       if (clear_user(buf, count))
                                return -EFAULT;
-               return count;
+                       read += count;
+                       break;
+               }
+
+               ignored = copy_to_user(buf, v, sz);
+               iounmap(v);
+               if (ignored)
+                       return -EFAULT;
+               buf += sz;
+               p += sz;
+               count -= sz;
+               read += sz;
        }
-       if (copy_to_user(buf, v, count))
-               goto out;
 
-       read = count;
        *ppos += read;
-out:
-       iounmap(v);
        return read;
 }
 
 static ssize_t write_mem(struct file * file, const char __user * buf, 
                         size_t count, loff_t *ppos)
 {
-       unsigned long p = *ppos;
-       ssize_t written = -EFAULT;
+       unsigned long p = *ppos, ignored;
+       ssize_t written = 0, sz;
        void __iomem *v;
 
-       if ((v = ioremap(p, count)) == NULL)
-               return -EFAULT;
-       if (copy_from_user(v, buf, count))
-               goto out;
+       while (count > 0) {
+               /*
+                * Handle first page in case it's not aligned
+                */
+               if (-p & (PAGE_SIZE - 1))
+                       sz = -p & (PAGE_SIZE - 1);
+               else
+                       sz = PAGE_SIZE;
+
+               sz = min_t(unsigned long, sz, count);
+
+               if ((v = ioremap(p, sz)) == NULL)
+                       break;
+
+               ignored = copy_from_user(v, buf, sz);
+               iounmap(v);
+               if (ignored) {
+                       written += sz - ignored;
+                       if (written)
+                               break;
+                       return -EFAULT;
+               }
+               buf += sz;
+               p += sz;
+               count -= sz;
+               written += sz;
+       }
 
-       written = count;
        *ppos += written;
-out:
-       iounmap(v);
        return written;
 }